﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;


namespace Less.Core
{
    /// <summary>
    /// Utility class for the lessframework database
    /// </summary>
    internal static class LessDatabaseUtils
    {
        private static readonly DateTime sqlDateMinValue = new DateTime(1900, 1, 1);
        private static bool? _prepared = false;// null= working, false= notPrepared, true = alreadyPrepared
        public static DateTime SqlDateMinValue
        {
            get
            {
                return sqlDateMinValue;
            }
        }
        /// <summary>
        /// Name of the connectionstring to use
        /// </summary>
        internal static string LessConnectionStringName
        {
            get
            {
                string name = ConfigurationManager.AppSettings["Data.LessConnectionStringName"];
                if (name == null)
                    name = "lessConnection";
                return name;
            }
        }
        internal static string DatabasePath { get; set; }
        internal static string DatabaseLogPath { get; set; }
        internal static long SqlSize { get; set; }
        /// <summary>
        /// null= working, false= notPrepared, true = alreadyPrepared
        /// </summary>
        private static bool? Prepared
        {
            get
            {
                return _prepared;
            }
            set
            {
                _prepared = value;
            }
        }
        private static string LessDatabaseJobs
        {
            get
            {
                string name = ConfigurationManager.AppSettings["Data.LessDatabaseJobs"];
                // TODO: Parametrize number of days
                if (name == null)
                    name = "DELETE FROM LogEntries WHERE Datediff(day,Created,GETDATE()) > 7 ";
                return name;
            }
        }
        /// <summary>
        /// Gets a new instance of the database manager
        /// </summary>
        /// <returns></returns>
        internal static void InitializeDatabase(IDatabase database)
        {
            try
            {
                if (Prepared.HasValue)
                {
                    if (!Prepared.GetValueOrDefault())
                    {
                        Log.Warning("Database needs to be prepared");
                        PrepareDatabase(database);
                    }
                }
                else
                {
                    Log.Debug("Database is being prepared", EntryCategories.LessDb);
                }
                Config.CanUseDatabase = true;
            }
            catch (Exception e)
            {
                Log.Fatal(e, EntryCategories.LessDb);
            }
        }
        private static string GetProviderName()
        {
            string providerName = string.Empty;
            ConnectionStringSettings connectionStringSettings =
            ConfigurationManager.ConnectionStrings[LessConnectionStringName];
            if (connectionStringSettings != null)
            {
                providerName = connectionStringSettings.ProviderName;
            }
            return providerName;
        }
        /// <summary>
        /// Verify or create, and update the database
        /// </summary>
        /// <param name="database"></param>
        private static void PrepareDatabase(IDatabase database)
        {
            Prepared = null; // To avoid concurrent calls to this method
            try
            {
                if (VerifySqlCeDatabase(database))
                {
                    if ( DoDatabaseJobs(database) )
                    {
                        Prepared = true;
                    }
                }
                else
                {
                    Prepared = false;
                }
            }
            catch (Exception e)
            {
                database = null;
                Log.Fatal(e, EntryCategories.LessDb);
                Prepared = false;
            }
        }
        private static bool VerifySqlCeDatabase(IDatabase database)
        {
            if (!GetProviderName().Contains("SqlServerCe"))
                return true;
            bool verified = false;
           
            try
            {
                var connectionString = database.ConnectionString;
                GetDatabasePaths(connectionString);
                ControlDatabaseSize();
            }
            catch (Exception e)
            {
                Log.Fatal(e, EntryCategories.LessDb);
            }
            return verified;
        }
        
        private static void GetDatabasePaths(string connectionString)
        {
            string initStr = "Source=";
            int init = connectionString.IndexOf(initStr);
            string endStr = ".sdf";
            int end = connectionString.IndexOf(endStr);
            DatabasePath = connectionString.Substring(init + initStr.Length, end - init - initStr.Length + endStr.Length);
            DatabaseLogPath = DatabasePath.Replace(".sdf", ".log");
        }
        private static void ControlDatabaseSize()
        {
            if (File.Exists(DatabasePath))
                SqlSize = new FileInfo(DatabasePath).Length;
            if (File.Exists(DatabaseLogPath))
                SqlSize += new FileInfo(DatabaseLogPath).Length;
            if (Status.ApplicationStatus != null)
            {
                Status.ApplicationStatus.SqlSize = SqlSize;
                Status.SaveStatus();
            }
        }
        
        private static bool DoDatabaseJobs(IDatabase database)
        {
            try
            {
                string databaseJobs = LessDatabaseJobs;
                if (databaseJobs.HasValue())
                {
                    string[] commands = databaseJobs.Split(';');
                    ExecuteCommands(database, commands);
                    Log.Info("DataBase Jobs Done!!", EntryCategories.LessDb);
                }
                return true;
            }
            catch (Exception e)
            {
                Log.Error(e, EntryCategories.LessDb);
                return false;
            }
        }
        
        private static bool ExecuteCommands(IDatabase database, IEnumerable<string> commands)
        {
            foreach (string command in commands)
            {
                if (command.HasValue())
                {
                    try
                    {
                        database.Execute(command);
                        Log.Info("OK: " + command, EntryCategories.LessDb);
                    }
                    catch (Exception e)
                    {
                        Log.Fatal(e, EntryCategories.LessDb);
                        return false;
                    }
                }
            }
            return true;
        }
    }
 }